package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class NIOServer {

    public static void main(String[] args) throws IOException {
        // 创建一个在本地端口进行监听的服务Socket通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //必须配置为非阻塞才能往selector上注册
        ssc.configureBlocking(false);
        //服务器绑定端口
        ssc.socket().bind(new InetSocketAddress(9000));
        // 创建选择器
        Selector selector = Selector.open();
        // 注册到选择器上，对客户端连接操作感兴趣
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 轮询监听channel里的key，select是阻塞的，获得客户端连接、读、写事件
            selector.select();
            System.out.println("事件发生");
            // 有客户端请求，被轮询监听到
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                //删除本次已处理的key，防止下次select重复处理
                it.remove();
                handle(key);
            }
        }
    }

    /**
     * 事件处理
     * @param key
     * @throws IOException
     */
    private static void handle(SelectionKey key) throws IOException {
        //处理连接事件
        if (key.isAcceptable()) {
            System.out.println("处理连接事件");
            //获得ServerSocketChannel
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            //获得ServerSocketChannel接收的客户端
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            //注册对SocketChannel读事件感兴趣
            sc.register(key.selector(), SelectionKey.OP_READ);
        }
        //处理读取事件
        else if (key.isReadable()) {
            System.out.println("处理读取事件");
            SocketChannel sc = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //read方法不会阻塞，调用时肯定是发生了客户端发送数据的事件
            int len = sc.read(buffer);
            if (len != -1) {
                System.out.println("读取到客户端发送的数据：" + new String(buffer.array(), 0, len));
            }
            //发送数据给客户端
            ByteBuffer bufferToWrite = ByteBuffer.wrap("Hello 客户端".getBytes());
            sc.write(bufferToWrite);
            key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        }
        //处理写事件
        else if (key.isWritable()) {
            SocketChannel sc = (SocketChannel) key.channel();
            System.out.println("处理写事件");
            key.interestOps(SelectionKey.OP_READ);
        }
    }
}
